#import "JVNetworkReachability.h"
#import "CoreTelephony/CTTelephonyNetworkInfo.h"
#import <SystemConfiguration/SystemConfiguration.h>
#import <netinet/in.h>

#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>

#import <CoreFoundation/CoreFoundation.h>

static SCNetworkReachabilityRef __reachability = nil;

JVNetworkReachability JVGetNetworkReachability() {
    if(!__reachability) {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
        struct sockaddr_in6 address;
        bzero(&address, sizeof(address));
        address.sin6_len = sizeof(address);
        address.sin6_family = AF_INET6;
#else
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_len = sizeof(address);
        address.sin_family = AF_INET;
#endif
        __reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&address);
    }
    
    SCNetworkReachabilityFlags flags;
	if (__reachability && SCNetworkReachabilityGetFlags(__reachability, &flags)) {
        if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        {
            // if target host is not reachable
            return JVNetworkReachabilityNone;
        }
        
        JVNetworkReachability retVal = JVNetworkReachabilityNone;
        
        if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
        {
            // if target host is reachable and no connection is required
            //  then we'll assume (for now) that your on Wi-Fi
            retVal = JVNetworkReachabilityWifi;
        }
        
        
        if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
             (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
        {
			// ... and the connection is on-demand (or on-traffic) if the
			//     calling application is using the CFSocketStream or higher APIs
            
			if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
			{
				// ... and no [user] intervention is needed
				retVal = JVNetworkReachabilityWifi;
			}
		}
        
        if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
        {
            // ... but WWAN connections are OK if the calling application
            //     is using the CFNetwork (CFSocketStream?) APIs.
            retVal = JVNetworkReachabilityMobile;
        }
        return retVal;
    }
    return JVNetworkReachabilityNone;
}

static CTTelephonyNetworkInfo * __telephonyNetworkInfo;
JVNetworkReachability JVGetAccurateNetworkReachability()
{
    JVNetworkReachability reachability = JVGetNetworkReachability();
    if (reachability == JVNetworkReachabilityMobile) {
        // fix-crash: https://stackoverflow.com/questions/33748737/coretelephony-crash-non-reproducible
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            __telephonyNetworkInfo = [CTTelephonyNetworkInfo new];
        });
        NSString * radioAccessTechnology = __telephonyNetworkInfo.currentRadioAccessTechnology;
        if ([radioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
            [radioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
            [radioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x])
            reachability = JVNetworkReachabilityMobile2G;
        else if ([radioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE])
            reachability = JVNetworkReachabilityMobile4G;
        else
            reachability = JVNetworkReachabilityMobile3G;
    }
    return reachability;
}
